/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com */

/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <assert.h>
#include <stdlib.h>

#include "events.h"


namespace tri
{

TAbstractEventComponent::TAbstractEventComponent() :
	m_MouseState(NONE), m_Enabled(true) {
}

TAbstractEventComponent::~TAbstractEventComponent() {
	
	if ( TEventsProcess::getSingleton()->_pressed_entity == this){
		TEventsProcess::getSingleton()->_pressed_entity = NULL;
	}
	
	if ( TEventsProcess::getSingleton()->_mouse_over_entity == this){
		TEventsProcess::getSingleton()->_mouse_over_entity = NULL;
	}
	
	if ( TEventsProcess::getSingleton()->_focus_entity == this){
		TEventsProcess::getSingleton()->_focus_entity = NULL;
	}
	
	if ( TEventsProcess::getSingleton()->_focus_requested_entity == this){
		TEventsProcess::getSingleton()->_focus_requested_entity = NULL;
	}

}

int TAbstractEventComponent::getEventState() {
	return m_MouseState;
}

bool TAbstractEventComponent::isEnabled() {
	return m_Enabled;
}
void TAbstractEventComponent::setEnabled(bool b) {
	m_Enabled = b;
}

void TAbstractEventComponent::setEventState(int state) {
	if(m_MouseState != state){
		m_MouseState = state;
	}
}

void TAbstractEventComponent::requestFocus(){
	TEventsProcess::requestFocus(this);
}

bool TAbstractEventComponent::hasFocus() {
	return TEventsProcess::getSingleton()->assertFocusEntity(this);
}

sigslot::signal0<sigslot::multi_threaded_local>* TAbstractEventComponent::getSlot0(int type){
	switch (type) {
		case tri::MOUSE_DRAG:
			return &this->MOUSE_DRAG;
		case tri::BUTTON_PRESSED:
		case tri::PRESSED:
			return &this->BUTTON_PRESSED;
		case tri::BUTTON_CLICKED:
		case tri::CLICKED:
			return &this->BUTTON_CLICKED;
		case tri::BUTTON_RELEASED:
		case tri::RELEASED:
			return &this->BUTTON_RELEASED;
		case tri::MOUSE_MOVE:
			return &this->MOUSE_MOVE;
		case tri::FOCUS_LOST:
			return &this->FOCUS_LOST;
		case tri::FOCUS_RECEIVED:
			return &this->FOCUS_RECEIVED;
		case tri::MOUSE_ENTER:
			return &this->MOUSE_ENTER;
		case tri::MOUSE_EXIT:
			return &this->MOUSE_EXIT;
		case tri::VALUE_CHANGED:
			return &this->VALUE_CHANGED;
		case tri::UPDATED:
			return &this->UPDATED;
	};
	
	return NULL;
}

sigslot::signal1<std::string, sigslot::multi_threaded_local>* TAbstractEventComponent::getSlotWT1(int type){
	switch (type) {
		case tri::MOUSE_DRAG:
			return &this->MOUSE_DRAG_WT;
		case tri::BUTTON_PRESSED:
		case tri::PRESSED:
			return &this->BUTTON_PRESSED_WT;
		case tri::BUTTON_CLICKED:
		case tri::CLICKED:
			return &this->BUTTON_CLICKED_WT;
		case tri::BUTTON_RELEASED:
		case tri::RELEASED:
			return &this->BUTTON_RELEASED_WT;
		case tri::MOUSE_MOVE:
			return &this->MOUSE_MOVE_WT;
		case tri::FOCUS_LOST:
			return &this->FOCUS_LOST_WT;
		case tri::FOCUS_RECEIVED:
			return &this->FOCUS_RECEIVED_WT;
		case tri::MOUSE_ENTER:
			return &this->MOUSE_ENTER_WT;
		case tri::MOUSE_EXIT:
			return &this->MOUSE_EXIT_WT;
		case tri::VALUE_CHANGED:
			return &this->VALUE_CHANGED_WT;
		case tri::UPDATED:
			return &this->UPDATED_WT;
	};
	
	return NULL;
}

sigslot::signal1<int,sigslot::multi_threaded_local>* TAbstractEventComponent::getSlot1(int type){
	switch (type) {
		case tri::KEY_PRESSED:
			return &this->KEY_PRESSED;
		case tri::KEY_RELEASED:
			return &this->KEY_RELEASED;
	};
	
	return NULL;
}

void TAbstractEventComponent::setTarget(std::string target){
	m_Target = target;
}

std::string TAbstractEventComponent::getTarget(){
	return m_Target;
}

void TAbstractEventComponent::emit(int event, int key) {
	if (!isEnabled()){
		return;
	}
	switch (event) {
	case tri::MOUSE_DRAG:
		this->MOUSE_DRAG.emit();
		this->MOUSE_DRAG_WT.emit(m_Target);
		break;
	case tri::BUTTON_PRESSED:
	case tri::PRESSED:
		this->BUTTON_PRESSED.emit();
		this->BUTTON_PRESSED_WT.emit(m_Target);
		break;
	case tri::BUTTON_CLICKED:
	case tri::CLICKED:
		this->BUTTON_CLICKED.emit();
		this->BUTTON_CLICKED_WT.emit(m_Target);
		break;
	case tri::BUTTON_RELEASED:
	case tri::RELEASED:
		this->BUTTON_RELEASED.emit();
		this->BUTTON_RELEASED_WT.emit(m_Target);
		break;
	case tri::MOUSE_MOVE:
		this->MOUSE_MOVE.emit();
		this->MOUSE_MOVE_WT.emit(m_Target);
		break;
	case tri::FOCUS_LOST:
		this->FOCUS_LOST.emit();
		this->FOCUS_LOST_WT.emit(m_Target);
		break;
	case tri::FOCUS_RECEIVED:
		this->FOCUS_RECEIVED.emit();
		this->MOUSE_DRAG_WT.emit(m_Target);
		break;
	case tri::MOUSE_ENTER:
		this->MOUSE_ENTER.emit();
		this->MOUSE_ENTER_WT.emit(m_Target);
		break;
	case tri::MOUSE_EXIT:
		this->MOUSE_EXIT.emit();
		this->MOUSE_EXIT_WT.emit(m_Target);
		break;
	case tri::KEY_PRESSED:
		this->KEY_PRESSED.emit(key);
		break;
	case tri::KEY_RELEASED:
		this->KEY_RELEASED.emit(key);
		break;
	case tri::VALUE_CHANGED:
		VALUE_CHANGED.emit();
		this->VALUE_CHANGED_WT.emit(m_Target);
		break;
	case tri::UPDATED:
		this->UPDATED.emit();
		this->UPDATED_WT.emit(m_Target);
		break;
	}
	
}

/**
 * 
 * EVENT PROCESS
 * 
 */


TEventsProcess* TEventsProcess::singleton = new TEventsProcess();

TEventsProcess* TEventsProcess::getSingleton() {
	return TEventsProcess::singleton;
}
TEventsProcess::TEventsProcess() :
	_pressed_entity(NULL), _mouse_over_entity(NULL), _focus_entity(NULL),
			_focus_requested_entity(NULL) {
}
TEventsProcess::~TEventsProcess() {
}

bool TEventsProcess::assertPressedEntity(TAbstractEventComponent* e) {
	return _pressed_entity==e;
}
bool TEventsProcess::assertFocusEntity(TAbstractEventComponent* e) {
	return _focus_entity==e;
}
bool TEventsProcess::assertMouseOverEntity(TAbstractEventComponent* e) {
	return _mouse_over_entity==e;
}
bool TEventsProcess::assertFocusRequestEntity(TAbstractEventComponent* e) {
	return _focus_requested_entity==e;
}

void TEventsProcess::process_mouse_event(const TMouseEvent& m, TAbstractEventComponent* e) {
	getSingleton()->_process_mouse_event(m, e);
}

void TEventsProcess::_process_mouse_event(const TMouseEvent& m, TAbstractEventComponent* e) {

	m.copy(m, TMouseEvent::s_CurrentEvent);
	
	/********************************************************************************
	 *       MOUSE DRAGGED
	 ********************************************************************************/
	if (m.event == MOUSE_DRAG) {
		if (_pressed_entity!=NULL && _pressed_entity==_mouse_over_entity) {
			if (_pressed_entity->isEnabled()) {
				_pressed_entity->setEventState(MOUSE_DRAG);
				TMouseEvent ev;
				TMouseEvent::copy(m, ev);
				ev.event = MOUSE_DRAG;
				_pressed_entity->emit(MOUSE_DRAG);

			}
		}
	}
	
	/********************************************************************************
	 *       MOUSE OVER
	 ********************************************************************************/

	if (_mouse_over_entity!=e && m.event != MOUSE_DRAG) {
		if (_mouse_over_entity!=NULL) {
			if (_mouse_over_entity->isEnabled()) {
				_mouse_over_entity->setEventState(NONE);
				TMouseEvent ev;
				TMouseEvent::copy(m, ev);
				ev.event = MOUSE_EXIT;
				_mouse_over_entity->emit(MOUSE_EXIT);

			}
		}

		_mouse_over_entity = e;

		if (_mouse_over_entity!=NULL) {
			if (_mouse_over_entity->isEnabled()) {
				_mouse_over_entity->setEventState(MOUSE_OVER);
				TMouseEvent ev;
				TMouseEvent::copy(m, ev);
				ev.event = MOUSE_ENTER;
				_mouse_over_entity->emit(MOUSE_ENTER);
			}
		}
	} else {
		if (_mouse_over_entity!=NULL) {
			if (_mouse_over_entity->isEnabled()) {
				if (_mouse_over_entity->getEventState()!=MOUSE_DRAG
						&& _mouse_over_entity->getEventState()!=BUTTON_PRESSED) {
					_mouse_over_entity->setEventState(MOUSE_OVER);
				}
			}
		}
	}

	if (_pressed_entity!=NULL) {
		if (_pressed_entity->getEventState()!=MOUSE_DRAG && _pressed_entity!=e) {
			_pressed_entity->setEventState(BUTTON_PRESSED);
		}
	}

	/********************************************************************************
	 *       MOUSE PRESSED
	 ********************************************************************************/
	if (m.event == BUTTON_PRESSED) {

		if (_pressed_entity != _mouse_over_entity) {
			if (_pressed_entity!=NULL) {
				if (_pressed_entity->isEnabled()) {
					_pressed_entity->setEventState(NONE);

				}
			}
		}

		_pressed_entity = e;
		
		if (_focus_entity!=_pressed_entity) {
			if (_focus_entity!=NULL) {
				_focus_entity->setEventState(NONE);
				_focus_entity->emit(FOCUS_LOST);
			}
			
		}

		if (_pressed_entity != NULL) {
			_focus_entity = _pressed_entity;
			if (_focus_entity != NULL) {
				_focus_entity->setEventState(NONE);
				_focus_entity->emit(FOCUS_RECEIVED);

			}
			
			if (_pressed_entity->isEnabled()) {
				_pressed_entity->setEventState(BUTTON_PRESSED);
				TMouseEvent ev;
				TMouseEvent::copy(m, ev);
				ev.event = BUTTON_PRESSED;
				_pressed_entity->emit(BUTTON_PRESSED);
			}

		} else {
			if (_focus_entity!=NULL) {
				_focus_entity->emit(FOCUS_LOST);
			}
			_focus_entity = NULL;
		}
	}

	/********************************************************************************
	 *       MOUSE RELEASED
	 ********************************************************************************/
	if (m.event == BUTTON_RELEASED) {
		if (e != NULL){
			if ( e->isEnabled()){
				TMouseEvent ev;
				TMouseEvent::copy(m, ev);
				ev.event = BUTTON_RELEASED;
				e->emit(BUTTON_RELEASED);
			}
		}
		//std::cout<<"release"<<std::endl;
		if (_pressed_entity!=NULL) {
			
			if (_pressed_entity==e) {
				if (_pressed_entity->isEnabled()) {
					_pressed_entity->setEventState(MOUSE_OVER);
					TMouseEvent ev;
					TMouseEvent::copy(m, ev);
					ev.event = BUTTON_CLICKED;
					_pressed_entity->emit(BUTTON_CLICKED);
					_pressed_entity=NULL;
				} else {
					_pressed_entity->setEventState(NONE);
					_pressed_entity=NULL;
				}

			} else {
				_pressed_entity->setEventState(NONE);
				_pressed_entity=NULL;
			}

		}
	}
	
	m.copy(m, TMouseEvent::s_LastEvent);
	
	if(_focus_requested_entity != NULL){
		if (_focus_entity!=NULL) {
			_focus_entity->setEventState(NONE);
			_focus_entity->emit(FOCUS_LOST);
		}
		
		_focus_entity = _focus_requested_entity;
		_focus_requested_entity = NULL;
		if (_focus_entity != NULL) {
			_focus_entity->setEventState(NONE);
			_focus_entity->emit(FOCUS_RECEIVED);
		}
	}
}

void TEventsProcess::process_key_event(const TKeyEvent& k) {
	getSingleton()->_process_key_event(k);
}

void TEventsProcess::_process_key_event(const TKeyEvent& k) {
	TKeyEvent ev;
	TKeyEvent::copy(k, ev);
	TKeyEvent::copy(k, TKeyEvent::s_CurrentEvent);

	if (ev.button>0&& ev.button<256) {
		switch (ev.event) {
		case KEY_PRESSED:
			getSingleton()->keys[ev.button] = true;
		case KEY_RELEASED:
			getSingleton()->keys[ev.button] = false;
		}
	}

	if (_focus_entity != NULL) {
		if (ev.getEvent()==KEY_PRESSED) {
			_focus_entity->emit(KEY_PRESSED, ev.getKey());
		} else {
			_focus_entity->emit(KEY_RELEASED, ev.getKey());
		}
	}
	
	
}

bool is_key_pressed(int button) {
	if (button>0&& button<256) {
		return TEventsProcess::is_key_pressed(button);
	}
	return false;
}

bool TEventsProcess::is_key_pressed(int button) {
	if (button>0&& button<256) {
		return TEventsProcess::getSingleton()->keys[button];
	}
	return false;
}

void TEventsProcess::requestFocus(TAbstractEventComponent* ent){
	if(ent != NULL){
		getSingleton()->_focus_requested_entity = ent;
	}
}

TMouseEvent getMouseEvent(){
	return TMouseEvent::getCurrentEvent();
}

}
